import { Injectable } from '@nestjs/common';
import * as dotenv from 'dotenv';
import * as Joi from '@hapi/joi';
import * as fs from 'fs';

export type EnvConfig = Record<string, string>;

@Injectable()
export class ConfigService {
  private readonly envConfig: EnvConfig;

  constructor(filePath: string) {
    const config = dotenv.parse(fs.readFileSync(filePath));
    this.envConfig = this.validateInput(config);
  }

  /**
   * Ensures all needed variables are set, and returns the validated JavaScript object
   * including the applied default values.
   */
  private validateInput(envConfig: EnvConfig): EnvConfig {
    const envVarsSchema: Joi.ObjectSchema = Joi.object({
      NODE_ENV: Joi.string()
        .valid('development', 'production', 'test', 'provision')
        .default('development'),
      PORT: Joi.number().default(3000),
      AUTH_ENABLED: Joi.boolean().required(),
      AUTH_TOKEN_EXPIRES_IN: [
        Joi.number().default(3600),
        Joi.string().pattern(
          /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/,
        ),
      ],
      AUTH_SECRET: Joi.string().required(),
      // PostgreSQL
      PG_HOST: [Joi.string().ip(), Joi.string().domain()],
      PG_PORT: Joi.number().default(5432),
      PG_USERNAME: Joi.string(),
      PG_PASSWORD: Joi.string(),
      PG_DATABASE: Joi.string(),
      PG_SCHEMA: Joi.string().default('public'),
    });

    const { error, value: validatedEnvConfig } = envVarsSchema.validate(
      envConfig,
    );
    if (error) {
      throw new Error(`Config validation error: ${error.message}`);
    }
    return validatedEnvConfig;
  }

  get nodeEnv(): string {
    return this.envConfig.NODE_ENV;
  }

  get port(): number {
    return Number(this.envConfig.PORT);
  }

  get isAuthEnabled(): boolean {
    return Boolean(this.envConfig.AUTH_ENABLED);
  }

  get authTokenExpiresIn(): string | number {
    return this.envConfig.AUTH_TOKEN_EXPIRES_IN;
  }

  get authSecret(): string {
    return this.envConfig.AUTH_SECRET;
  }

  get pgHost(): string {
    return this.envConfig.PG_HOST;
  }

  get pgPort(): number {
    return Number(this.envConfig.PG_PORT);
  }

  get pgUsername(): string {
    return this.envConfig.PG_USERNAME;
  }

  get pgPassword(): string {
    return this.envConfig.PG_PASSWORD;
  }

  get pgDatabase(): string {
    return this.envConfig.PG_DATABASE;
  }

  get pgSchema(): string {
    return this.envConfig.PG_SCHEMA;
  }
}
